/*
 * Copyright 2012 Finbudget.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.user.cellview.client;

import com.google.gwt.dom.client.*;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.view.client.ProvidesKey;

import java.util.Set;

public class NavigatableCellTable<T> extends CellTable<T> {
    
    private final com.google.gwt.user.client.EventListener eventListener = new com.google.gwt.user.client.EventListener() {
        @Override
        public void onBrowserEvent(Event event) {
            String type = event.getType();
            if (BrowserEvents.KEYDOWN.equals(type) && !isKeyboardNavigationSuppressed()) {
                int keyCode = event.getKeyCode();
                switch (keyCode) {
                    case KeyCodes.KEY_HOME:
                        if (event.getCtrlKey()) {
                            keyboardCtrlHome();
                        } else {
                            selectFirstColumn();
                        }
                        event.preventDefault();
                        return;
                    case KeyCodes.KEY_END:
                        if (event.getCtrlKey()) {
                            keyboardCtrlEnd();
                        } else {
                            selectLastColumn();
                        }
                        event.preventDefault();
                        return;
                    case KeyCodes.KEY_PAGEDOWN:
                        keyboardPageDown();
                        event.preventDefault();
                        return;
                    case KeyCodes.KEY_PAGEUP:
                        keyboardPageUp();
                        event.preventDefault();
                        return;
                    case KeyCodes.KEY_DOWN:
                    	setSelectedNextRow();
                    	event.preventDefault();
                    	return;
                    case KeyCodes.KEY_UP:
                    	setSelectedPrevRow();
                    	event.preventDefault();
                    	return;
                }
            }
            NavigatableCellTable.this.onBrowserEvent(event);
        }
    };

    @Override
    protected void onLoad() {
        super.onLoad();
        DOM.setEventListener(getElement(), eventListener);
    }

    public NavigatableCellTable(int pageSize, Resources resources, ProvidesKey<T> keyProvider) {
        super(pageSize, resources, keyProvider);
    }

    protected void setSelectedPrevRow() {
    	HasDataPresenter<?> presenter = getPresenter();
    	presenter.setKeyboardSelectedRow(presenter.getKeyboardSelectedRow() - 1, true, false);
//        getPresenter().keyboardPrev();
    }

    protected void setSelectedNextRow() {
    	HasDataPresenter<?> presenter = getPresenter();
    	presenter.setKeyboardSelectedRow(presenter.getKeyboardSelectedRow() + 1, true, false);
//        getPresenter().keyboardNext();
    }

    protected void keyboardPageDown() {
        HasDataPresenter<?> presenter = getPresenter();
        presenter.setKeyboardSelectedRow(presenter.getKeyboardSelectedRow() + getPageSize(), true, false);
    }

    protected void keyboardPageUp() {
        HasDataPresenter<?> presenter = getPresenter();
        presenter.setKeyboardSelectedRow(presenter.getKeyboardSelectedRow() - getPageSize(), true, false);
    }

    protected int getKeyboardSelectedColumnIndex() {
        Element element = getKeyboardSelectedElement();
        TableCellElement td = element.getParentElement().cast();
        return td.getCellIndex();
    }

    protected void setKeyboardSelectedCell(final int row, final int col, boolean stealFocus, boolean forceUpdate) {
        getPresenter().setKeyboardSelectedRow(row, stealFocus, forceUpdate);
        int currColumnIndex = getKeyboardSelectedColumnIndex();
        while (currColumnIndex != col) {
            NativeEvent event = Document.get().createKeyDownEvent(false, false, false, false,
                    currColumnIndex > col ? KeyCodes.KEY_LEFT : KeyCodes.KEY_RIGHT);
            getKeyboardSelectedElement().dispatchEvent(event);
            currColumnIndex = getKeyboardSelectedColumnIndex();
        }
    }

    public void selectNextColumn() {
        for (int i = getKeyboardSelectedColumnIndex() + 1; i < getColumnCount(); i++) {
            if (isColumnInteractive(getColumn(i))) {
                setKeyboardSelectedCell(getKeyboardSelectedRow(), i, true, true);
                return;
            }
        }
    }

    public void selectPrevColumn() {
        for (int i = getKeyboardSelectedColumnIndex() - 1; i >= 0; i--) {
            if (isColumnInteractive(getColumn(i))) {
                setKeyboardSelectedCell(getKeyboardSelectedRow(), i, true, true);
                return;
            }
        }
    }

    /**
     * Check if a column consumes events.
     */
    private boolean isColumnInteractive(Column<T, ?> column) {
        Set<String> consumedEvents = column.getCell().getConsumedEvents();
        return consumedEvents != null && consumedEvents.size() > 0;
    }

    /**
     * Find and return the index of the first interactive column. If no column is
     * interactive, -1 is returned.
     */
    private int findFirstColumn() {
        for (int i = 0; i < getColumnCount(); i++) {
            if (isColumnInteractive(getColumn(i))) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Find and return the index of the last interactive column. If no column is
     * interactive, -1 is returned.
     */
    private int findLastColumn() {
        for (int i = getColumnCount() - 1; i >= 0; i--) {
            if (isColumnInteractive(getColumn(i))) {
                return i;
            }
        }
        return -1;
    }

    public void selectFirstColumn() {
        int firstColumnIndex = findFirstColumn();
        if (firstColumnIndex >= 0) {
            setKeyboardSelectedCell(getKeyboardSelectedRow(), firstColumnIndex, true, true);
        }
    }

    public void selectLastColumn() {
        int lastColumnIndex = findLastColumn();
        if (lastColumnIndex >= 0) {
            setKeyboardSelectedCell(getKeyboardSelectedRow(), lastColumnIndex, true, true);
        }
    }

    public void keyboardCtrlHome() {
        int firstColumnIndex = findFirstColumn();
        if (firstColumnIndex >= 0) {
            setKeyboardSelectedCell(-getPageStart(), firstColumnIndex, false, true);
        }
    }

    public void keyboardCtrlEnd() {
        int lastColumnIndex = findLastColumn();
        if (lastColumnIndex >= 0) {
            setKeyboardSelectedCell(getRowCount() - 1, lastColumnIndex, false, true);
        }
    }
}
